package com.xohm.platform.api;

import com.sun.jna.Native;
import com.xohm.base.Properties;
import com.xohm.base.logging.XohmLogger;
import com.xohm.platform.Kernel32_dll;
import com.xohm.platform.WindowsRegUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;

/**
 * Xohm Connection Manager
 *
 * Licensed Material - Property of Sprint Nextel
 * Restricted Materials of Sprint Nextel
 * (C) Copyright Sprint Nextel Corp. 2008 - All Rights Reserved.
 *
 * This class contains the methods for checking an existing CM is
 * running and also for the location of the WiMax drivers on the
 * Windows system.
 *
 * @author Robin Katzer
 */

public class OSAPIWindows extends OSAPIPidLookup implements OSAPIInterface
{
	private Kernel32_dll kernel32 = null;
	private Vector<DriverInformation> drivers = null;

	public OSAPIWindows()
	{
		try
		{
			String osRoot = System.getenv("SystemRoot");
			kernel32 = (Kernel32_dll)Native.loadLibrary(osRoot+"/system32/kernel32.dll", Kernel32_dll.class);
		}
		catch (Exception e)
		{
			XohmLogger.fatalPrintln(e.toString(), null, null);
			e.printStackTrace();
		}
	}
	
	/**
	 * This method attempt to retrieve this applications process id.
	 * 
	 * @param pidToFind int process id to find
	 * @return boolean
	 */
	public boolean isProcessRunning(int pidToFind)
	{
		boolean exists = false;
		
		try
		{
			int results = kernel32.GetProcessVersion(pidToFind);
			if (results > 1) exists = true;
		}
		catch (Exception e)
		{
			XohmLogger.fatalPrintln("Exception in "+this.getClass().getName()+".isProcessRunning(int): "+e.toString(), null, null);
		}

		return exists;
	}
	
	/**
	 * This method attempt to retrieve this applications process id.
	 * 
	 * @return int process id
	 */
	public int getProcessId()
	{
		int pid = -1;
		
		String result = ManagementFactory.getRuntimeMXBean().getName();
		if (result != null)
		{
			int end = result.indexOf("@");
			if (end > 0)
			{
				result = result.substring(0, end);
				if (result != null && !result.trim().equals(""))
					pid = Integer.valueOf(result).intValue();
			}
		}
		
		return pid;
	}
	
	public static void main(String[] args)
	{
		OSAPIWindows api = new OSAPIWindows();

		// Just need to check to see if there is another process running
		System.out.println(api.doesProcessExist());
		
		// Get Registry entries for all drivers extension and path
		Vector results = api.getAvailableDriverInformation();
		System.out.println(results);
	}
	
	/**
	 * Hard code the two paths for now, need to come back and iterate over the
	 * sub-nodes so that drivers can be added without having to make changes
	 * in this code.
	 * 
	 * Drivers are located in the Windows registry at:
	 * HKEY_LOCAL_MACHINE\\SOFTWARE\\WiMax\\CommonAPI\\Libs
	 * 
	 * More specifically:
	 * HKEY_LOCAL_MACHINE\\SOFTWARE\\WiMax\\CommonAPI\\Libs\\Samsung Electronics\\PCI Express
	 * HKEY_LOCAL_MACHINE\\SOFTWARE\\WiMax\\CommonAPI\\Libs\\ZTE\\TU25_USB_MODEM_BCM
	 * 
	 * Values are:
	 * Path=
	 * Extension=
	 *
	 */
	public Vector<DriverInformation> getAvailableDriverInformation()
	{
		if (drivers == null)
		{
			String commonApiLocation = "SOFTWARE\\WiMax\\CommonAPI\\Libs";
			String[] vendorKeys = getVendorKeys(commonApiLocation);
			if (vendorKeys != null)
			{
				Vector<String> deviceKeys = getDeviceKeys(vendorKeys);
				drivers = getDrivers(deviceKeys);
			}
		}
		
		if (drivers == null)
			drivers = new Vector<DriverInformation>();

		return drivers;
	}
	
	private String[] getVendorKeys(String commonApiLocation)
	{
		int[] ret = WindowsRegUtil.RegOpenKey(
				WindowsRegUtil.HKEY_LOCAL_MACHINE, commonApiLocation,
				WindowsRegUtil.KEY_READ);
		int handle = ret[WindowsRegUtil.NATIVE_HANDLE];
		int[] retInfo = WindowsRegUtil.RegQueryInfoKey(handle);

		// Get Vendor Keys
		String[] vendorKeys = null;
		if (retInfo[WindowsRegUtil.SUBKEYS_NUMBER] > 0)
		{
			vendorKeys = new String[retInfo[WindowsRegUtil.SUBKEYS_NUMBER]];
			for (int i = 0; i < retInfo[WindowsRegUtil.SUBKEYS_NUMBER]; i++)
			{
				byte[] nameOfKey = WindowsRegUtil.RegEnumKeyEx(handle, i,
						retInfo[WindowsRegUtil.MAX_KEY_LENGTH] + 1);
				if (nameOfKey != null)
				{
					vendorKeys[i] = commonApiLocation + "\\" + (WindowsRegUtil.bytesToString(nameOfKey).substring(0, nameOfKey.length - 1));
				}
			}
		}
		WindowsRegUtil.RegCloseKey(handle);
		
		return vendorKeys;
	}
	
	private Vector<String> getDeviceKeys(String[] vendorKeys)
	{
		Vector<String> deviceKeys = new Vector<String>();
		for (int i = 0; i < vendorKeys.length; i++)
		{
			int[] ret = WindowsRegUtil.RegOpenKey(
					WindowsRegUtil.HKEY_LOCAL_MACHINE, vendorKeys[i],
					WindowsRegUtil.KEY_READ);
			int handle = ret[WindowsRegUtil.NATIVE_HANDLE];
			int[] retInfo = WindowsRegUtil.RegQueryInfoKey(handle);

			for (int j = 0; j < retInfo[WindowsRegUtil.SUBKEYS_NUMBER]; j++)
			{
				byte[] nameOfKey = WindowsRegUtil.RegEnumKeyEx(handle, j,
						retInfo[WindowsRegUtil.MAX_KEY_LENGTH] + 1);
				if (nameOfKey != null)
				{
					deviceKeys.addElement(vendorKeys[i] + "\\" + WindowsRegUtil.bytesToString(nameOfKey).substring(0, nameOfKey.length - 1));
				}
			}
			WindowsRegUtil.RegCloseKey(handle);
		}
		
		return deviceKeys;
	}
	
	private Vector<DriverInformation> getDrivers(Vector<String> deviceKeys)
	{
		drivers = new Vector<DriverInformation>();

		// Get the device Values
		for (int i = 0; i < deviceKeys.size(); i++)
		{
			int[] ret = WindowsRegUtil.RegOpenKey(WindowsRegUtil.HKEY_LOCAL_MACHINE, deviceKeys.elementAt(i), WindowsRegUtil.KEY_READ);
			int handle = ret[WindowsRegUtil.NATIVE_HANDLE];

			drivers.addElement(getValues(handle));

			WindowsRegUtil.RegCloseKey(handle);
		}
		
		return drivers;
	}

	private DriverInformation getValues(int handle)
	{
      // get the Number of Values in the key
      int[] info = WindowsRegUtil.RegQueryInfoKey(handle);   
      int count = info[WindowsRegUtil.VALUES_NUMBER];   
      int maxlen = info[WindowsRegUtil.MAX_VALUE_NAME_LENGTH];   

      boolean entryFound = false;
      DriverInformation driverInfo = new DriverInformation();
      for(int i=0;i<count;i++)
      {
          // get the Name of a key
          // Note to use 1 greater than the length returned by query
          byte[] name = WindowsRegUtil.RegEnumValue(handle, i, maxlen+1);
          
          // Get its Value
          byte[] values = WindowsRegUtil.RegQueryValueEx(handle, name);
          if(values != null && !new String(values).trim().equals(""))
          {
        	  entryFound = true;
        	  if (new String(name).trim().equals("Extension"))
        		  driverInfo.setDriverName(new String(values).trim());
         	 
        	  if (new String(name).trim().equals("Path"))
        		  driverInfo.setDriverLocation(new String(values).trim());
          }
      }
      if (!entryFound) driverInfo = null;
      
      return driverInfo;
	}
	
	/**
	 * Open the URL specified.<br><br>
	 * 
	 * @param url java.lang.String
	 */
	public void openUrl(String url)
	{
		try
		{ 
			Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
		}
		catch (Exception ex)
		{
			XohmLogger.warningPrintln("Could not open the url: " + url + " - Reason:" + ex.getMessage(), null, null);
		}
	}
	
	/**
	 * Returns the matched line containing default gateway and interface.
	 * 
	 * @return MatchResult - matched line
	 */
	private MatchResult getMatchedLine() {
		MatchResult result = null;
        String _255 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
        String exIP = "(?:" + _255 + "\\.){3}" + _255;
 
        // Regexp to find the good line
        Pattern pat = Pattern.compile("^\\s*(?:0\\.0\\.0\\.0\\s*){1,2}("+exIP+")\\s*("+exIP+").*");
        Process proc;
        try {
            // netstat command
            proc = Runtime.getRuntime().exec("netstat -rn");
       
            InputStream inputstream = proc.getInputStream();
            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
    
            // Parsing the result
            String line;
            while ((line = bufferedreader.readLine()) != null) {
                Matcher m = pat.matcher(line);
                if(m.matches()){                	
                	result = m.toMatchResult();
                	break;
                }
            }
        } catch (IOException ex) {
        	XohmLogger.warningPrintln("Exception occured while retrieving the Gateway IP", null, null);
        }
        
        return result;
	}
	
	/**
	 * This method returns the default gateway ip address.
	 */
	public String getGatewayIP() {
		String gatewayIP = "";
		MatchResult result = getMatchedLine();
        if(result != null && result.groupCount() > 0)
        	gatewayIP = result.group(1);
        
        return gatewayIP;
	}
	
	/**
	 * This method returns the currently active interface.
	 */
	public String getActiveInterface() throws UnknownHostException {
		String activeInterface = "";
		String localIP = "";
		MatchResult result = getMatchedLine();
		if(result != null && result.groupCount() > 1)
			localIP = result.group(2);
		
		try {
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); 
			while(interfaces.hasMoreElements()) {
				NetworkInterface ni = (NetworkInterface) interfaces.nextElement();
	            Enumeration<InetAddress> e2 = ni.getInetAddresses();
	            while (e2.hasMoreElements()){
	               String ip = ((InetAddress) e2.nextElement()).toString().split("/")[1];
	               if(localIP.equals(ip)) {
	            	   activeInterface = ni.getDisplayName();	            	   
	            	   break;
	               }	               
	            }
	        }
		}catch (Exception ex) {
	    	ex.printStackTrace();
	    }
		
        if("".equals(activeInterface) || "".equals(localIP)) {
        	throw new UnknownHostException("Active Interface not found.");
        }
               	        
        return activeInterface + "/" + localIP;
	}
	
	/**
	 * This method performs the ping operation for the specified target 
	 * using the specified options.
	 * 
	 * @param target String - ping target
	 * @param count int - number of packets
	 * @param packetSize int - size of ping packet
	 * @param timeout int - timeout period
	 */
	public Process ping(String target, int count, int packetSize, int timeout) {
		String cmd = "ping -n " + count + " -l " + packetSize +
		" -w " + timeout + " " + target;
		
		Process pingProc = null;
		try {			
			pingProc = Runtime.getRuntime().exec(cmd);
		}catch(Exception ex) {
			XohmLogger.warningPrintln("Exception occured when pinging the target " + target, null, null);
		}	

		return pingProc;
	}
	
	/**
	 * This method returns the mac address for the specified interface name.
	 * 
	 * @param interfaceName String - name of interface.
	 */
	public String getMacAddress(String interfaceName) {
		String macAddress = "";
		try {			
			Process pingProc = Runtime.getRuntime().exec("ipconfig /all");
			InputStream stream = pingProc.getInputStream();
			BufferedReader in = new BufferedReader(new InputStreamReader(stream, "UTF-8"));			
	    	String line;
	    	boolean adapterFound = false;
	    	String shortInterfaceName = interfaceName.split("-")[0].trim();    		
	    	while ((line = in.readLine()) != null) {	    		
	    		if(line.contains(shortInterfaceName.subSequence(0, shortInterfaceName.length()-1))) {	    			
	    			adapterFound = true;
	    		}
    			if(adapterFound && line.contains("Physical Address".subSequence(0, 15))) {
		    		macAddress = line.split(":")[1].replaceAll("-", ":").trim();
		    		break;
	    		}
		    }
		}catch(Exception ex) {
			XohmLogger.warningPrintln("Exception occured in retrieveing the MAC address for interface " + interfaceName, null, null);
		}
		return macAddress;
	}
	
	/**
	 * This method add a listener for the device insert notification.
	 * 
	 * @param callbackObj Object - callback object
	 * @param callbackMethodName String - callback method name
	 */
	public void addDeviceInsertListener(Object callbackObj, String callbackMethodName) {
		OSAPICommonHelper helper = new OSAPICommonHelper();
		helper.addDeviceInsertListener(callbackObj, callbackMethodName);
	}	
	
	/**
	 * This method updates the maximum size used for native string datatypes.
	 * It should be called before doing any native api calls.
	 */
	public void setMaxSizeForNativeTypes() {
		Properties.MAX_SIZE_OF_NSP_NAME = 129;
		Properties.MAX_SIZE_OF_STRING_BUFFER = 256;
	}

}
